import qs from "qs";
import interceptor from "./interceptor";
// 原生非Html页面前缀
let APP_SCHEMA_NATIVE = `uniapp://page`;
// 原生内嵌Html前缀
let APP_SCHEMA_HTML = `uniapp://html`;
// 小程序
let APP_SCHEMA_WXMINIAPP = `uniapp://wxmp`;
// webview页面地址
let APP_WEBVIEW_PATH = null;

let INTERCEPTORS = { ...interceptor };

let pageConfigInstance = null;

// app页面路径
let APP_PAGE_ROUTES = {};
// app tabbar路径，以区分跳转使用swithTab
let APP_TABBAR_PAGES = [];

export const REGEX_HTTP_PREFIX = /^(http|https):\/\/.*/;

export function isExternal(url) {
  return REGEX_HTTP_PREFIX.test(url);
}

const navigate = (navFn, options, beforeMessage) => {
  const sleep = (ts) =>
    new Promise((resolve) => setTimeout(() => resolve(), ts));
  // eslint-disable-next-line no-async-promise-executor
  return new Promise(async (resolve, reject) => {
    if (beforeMessage) {
      uni.showToast({
        title: beforeMessage,
        icon: "none",
      });
      await sleep(1500);
    }

    navFn({
      ...options,

      success(res) {
        const [, queryStr] = (options.url || "").split("?");
        const query = qs.parse(queryStr, { ignoreQueryPrefix: true }) || {};
        if (options.isBack && options.onBackEventName) {
          uni.$emit(options.onBackEventName, {
            ...query,
            ...(options.query || {}),
          });
        }
        options.success && options.success(res);
        resolve(res);
      },
      fail(error) {
        options.fail && options.fail(error);
        reject(error);
      },
    });
  });
};
// 跳转小程序
function navigateWxMiniApp(userNamePath, routeParams, params) {
  wx.navigateToMiniProgram({
    appId: params.appId,
    path: `${userNamePath}?${qs.stringify(routeParams)}`,
    extraData: decodeURIComponent(params.extMsg || "{}"),
    envVersion: params.miniProgramType,
    fail() {
      uni.showModal({
        title: "提示",
        content: "跳转第三方小程序失败， 是否重试",
        confirmText: "重试",
        cancelText: "取消",
        confirm() {
          navigateWxMiniApp(userNamePath, params);
        },
      });
    },
  });
}

const parseNativeRoute = (routeUrl, originRouteUrl) => {
  if (!routeUrl || !routeUrl.trim()) {
    return "";
  }
  const queryIndex = routeUrl.indexOf("?");
  const nativeRoute = routeUrl.slice(
    0,
    queryIndex > -1 ? queryIndex : undefined
  );
  const queryText = queryIndex > -1 ? routeUrl.slice(queryIndex) : "";
  const matchRoute = navigator.nativeRouteMap[nativeRoute];
  if (typeof matchRoute === "string") {
    return `${matchRoute}${queryText}`;
  }
  if (typeof matchRoute === "function") {
    return matchRoute(routeUrl, qs.parse(queryText || ""), originRouteUrl);
  }
  // 均为匹配配置，如果匹配小程序原生地址，返回
  if (APP_PAGE_ROUTES[matchRoute]) {
    return routeUrl;
  }
  return "";
};
/**
 * 解析Route地址
 * @param {*} routeUrl 后端传入的route地址，可以是AppSchema、网页链接
 * @returns 对应页面地址及参数字符串
 */
const parseRouteUrl = (routeUrl) => {
  if (!routeUrl) {
    return null;
  }
  // 项目内部真实地址
  if (routeUrl.indexOf("/") === 0) {
    return routeUrl;
  }
  // 原生地址
  if (routeUrl.indexOf(APP_SCHEMA_NATIVE) === 0) {
    const nativeRoute = routeUrl.slice(APP_SCHEMA_NATIVE.length);
    return parseNativeRoute(nativeRoute, routeUrl);
  }
  // 小程序
  if (routeUrl.indexOf(APP_SCHEMA_WXMINIAPP) === 0) {
    return routeUrl.slice(APP_SCHEMA_WXMINIAPP.length);
  }
  // 内嵌H5地址
  if (routeUrl.indexOf(APP_SCHEMA_HTML) === 0) {
    return `${APP_WEBVIEW_PATH}${routeUrl.slice(APP_SCHEMA_HTML.length)}`;
  } else if (isExternal(routeUrl)) {
    return `${APP_WEBVIEW_PATH}?url=${encodeURIComponent(routeUrl)}`;
  }
  console.warn("请传入合法的地址");
  return null;
};
function getPageInStack(url) {
  const pages = getCurrentPages().reverse();
  const index = pages.findIndex((page) => url === `/${page.route}`);
  if (index > 0) {
    return [index, pages[index]];
  }
  return [-1, null];
}
export const navigator = {
  nativeRouteMap: {},
  setAppSchemaNative(appSchemaNative) {
    APP_SCHEMA_NATIVE = appSchemaNative || APP_SCHEMA_NATIVE;
    return this;
  },
  setAppSchemaHtml(appSchemaHtml) {
    APP_SCHEMA_HTML = appSchemaHtml || APP_SCHEMA_HTML;
    return this;
  },
  setAppSchemaWXminiApp(appSchemaWXminiApp) {
    APP_SCHEMA_WXMINIAPP = appSchemaWXminiApp || APP_SCHEMA_WXMINIAPP;
    return this;
  },
  setAppWebviewPath(appWebviewPath) {
    APP_WEBVIEW_PATH = appWebviewPath || APP_WEBVIEW_PATH;
    return this;
  },
  setPages(pages) {
    pageConfigInstance = pages;
    return this;
  },
  setInterceptors(interceptors) {
    INTERCEPTORS = interceptors || INTERCEPTORS;
    return this;
  },
  init() {
    APP_PAGE_ROUTES = pageConfigInstance.pages
      .concat(
        (pageConfigInstance.subPackages || []).reduce((result, subPackItem) => {
          return result.concat(
            (subPackItem.pages || []).map((subPage) => ({
              ...subPage,
              path: `${subPackItem.root}/${subPage.path}`,
            }))
          );
        }, [])
      )
      .reduce((result, pageItem) => {
        result[`/${pageItem.path}`] = {
          title: (pageItem.style || {}).navigationBarTitleText || "",
          permitType: pageItem.permitType,
        };
        return result;
      }, {});
    APP_TABBAR_PAGES = ((pageConfigInstance.tabBar || {}).list || []).map(
      (tabItem) => `/${tabItem.pagePath}`
    );
    return this;
  },

  /**
   * 跳转路由封装
   * @param {String|Object} options 跳转配置对象或者路由地址
   * @param {String} options.url 跳转路由地址
   * @param {Function} options.query 跳转路由参数
   * @param {String} options.animationType 窗口显示的动画效果
   * @param {Number} options.animationDuration 窗口显示的动画效果
   * @param {Object} options.events 页面间通信接口，用于监听被打开页面发送到当前页面的数据
   * @param {Function} options.success 窗口显示的动画效果
   * @param {Function} options.fail 窗口显示的动画效果
   * @param {Function} options.onBack 窗口显示的动画效果
   * @param {Function} options.complete 窗口显示的动画效果
   * @param {Function} options.permitCallBack 权限处理回调，此回调存在不会执行默认权限校验失败拦截处理
   * @param {String} beforeMessage 跳转前的提示，在提示后才会进行跳转
   * @param {Bolean} isRedirect 是否是重定向
   * @param {Bolean} isReLaunch 是否是关闭所有页面，打开到应用内的某个页面
   * @returns
   */
  async navigateTo(options, beforeMessage, isRedirect) {
    let isReLaunch = false;
    if (typeof options === "string") {
      options = {
        url: options,
      };
    } else {
      beforeMessage = beforeMessage || options.beforeMessage;
      isRedirect = isRedirect || options.isRedirect;
      isReLaunch = options.isReLaunch || false;
    }
    // 1. 解析路由地址为项目页面路径加参数
    // 2. 解析参数
    // 3. 权限判断处理
    // 4. TODO 埋点增加
    const permitCallBack = options.permitCallBack;
    const urlQueryIndex = options.url.indexOf("?");
    const routeMatchPath = parseRouteUrl(options.url);
    const routeMatchPage =
      urlQueryIndex > -1
        ? routeMatchPath.slice(0, routeMatchPath.indexOf("?"))
        : routeMatchPath;
    const routeParams =
      urlQueryIndex > -1 ? qs.parse(options.url.slice(urlQueryIndex + 1)) : {};
    const permitType =
      (routeParams || {}).permitType ||
      (APP_PAGE_ROUTES[routeMatchPage] || {}).permitType ||
      0;
    const userInfo = (getApp().globalData || {}).userInfo || {};
    const query = options.query || {};
    const extraQueryText =
      query && Object.keys(query).length
        ? `${urlQueryIndex > -1 ? "&" : "?"}${qs.stringify(query)}`
        : "";
    if (!routeMatchPath) {
      return;
    }
    const noPermissionDefaultHandle = (messageType, message, failToPath) => {
      const successHandle = () => {
        const pageStackList = getCurrentPages();
        let lastPageRoute = null;
        if (pageStackList && pageStackList.length > 0) {
          lastPageRoute = pageStackList[pageStackList.length - 1];
        }
        const routeParams = {
          url: `${failToPath}?fromPath=${encodeURIComponent(
            (lastPageRoute.$page || {}).fullPath || ""
          )}&successPath=${encodeURIComponent(
            routeMatchPath + extraQueryText
          )}`,
        };
        if (isRedirect) {
          uni.redirectTo(routeParams);
        } else if (isReLaunch) {
          uni.reLaunch(routeParams);
        } else {
          uni.navigateTo(routeParams);
        }
      };
      if (messageType === "toast") {
        uni.showToast({
          title: message,
          icon: "none",
          success: () => {
            setTimeout(successHandle, 1500);
          },
        });
      } else {
        uni.showModal({
          content: message,
          success: (res) => {
            res.confirm && successHandle();
          },
        });
      }
    };

    const passAllAssert =
      !permitType ||
      Object.keys(INTERCEPTORS)
        .sort()
        .every((permitCode) => {
          permitCode = Number(permitCode);
          const interceptorItem = INTERCEPTORS[permitCode];
          if (
            permitType >= permitCode &&
            !interceptorItem.canNext(permitType, userInfo)
          ) {
            permitCallBack && permitCallBack(permitCode, routeMatchPath);
            !permitCallBack &&
              noPermissionDefaultHandle(
                interceptorItem.feedType,
                interceptorItem.message,
                interceptorItem.failToPath
              );
            return false;
          }
          return true;
        });
    if (!passAllAssert) {
      return null;
    }
    // 小程序
    if (options.url.indexOf(APP_SCHEMA_WXMINIAPP) === 0) {
      navigateWxMiniApp(routeMatchPage, routeParams, options.query);
      return;
    }
    const isTabBarPage = APP_TABBAR_PAGES.indexOf(routeMatchPage) > -1;
    const navFunc = isTabBarPage
      ? uni.switchTab
      : isReLaunch
      ? uni.reLaunch
      : uni.navigateTo;
    const { onBack = () => {}, events = {}, ...restOptions } = options;

    return await navigate(
      isRedirect && !isTabBarPage ? uni.redirectTo : navFunc,
      {
        ...restOptions,
        url: `${routeMatchPath}${extraQueryText}`,
        events: {
          ...events,
          onBack,
        },
      },
      beforeMessage
    );
  },
  /**
   * 返回
   * @param {*} options
   * @returns
   */
  async navigateBack(options = {}, beforeMessage, eventChannel) {
    const pages = getCurrentPages().reverse();
    options =
      typeof options === "string" ? { beforeMessage: options } : options || {};
    const delta =
      (options.delta || 1) >= pages.length
        ? pages.length - 1
        : options.delta || 1;
    const page = pages[delta];
    return await navigate(
      uni.navigateBack,
      {
        ...options,
        isBack: true,
        onBackEventName: page.$vm.onBackEventName,
        success(ret) {
          if (eventChannel) {
            eventChannel.emit("onBack", options.backParams);
          }
        },
      },
      options.beforeMessage || beforeMessage
    );
  },
  /**
   * 返回指定页面，页面栈没有该页面执行跳转
   * @param {*} options 返回页面路径或者对象
   * @param {String} options.url 返回页面路径
   */
  async navigateBackTo(options, beforeMessage) {
    if (typeof options === "string") {
      options = {
        url: options,
      };
    } else {
      beforeMessage = beforeMessage || options.beforeMessage;
    }
    options.url = options.url || "";
    const queryIndex = options.url.indexOf("?");
    const routePath =
      queryIndex > -1 ? options.url.slice(0, queryIndex) : options.url;
    const [index, page] = getPageInStack(routePath);
    if (index >= 0) {
      options.delta = index;
      return await navigate(
        uni.navigateBack,
        { ...options, isBack: true, onBackEventName: page.$vm.onBackEventName },
        beforeMessage
      );
    }
    return await this.navigateTo(options, beforeMessage);
  },
  // 跳转H5
  async navigateToH5(path, title, beforeMessage) {
    return await navigator.navigateTo(
      {
        url: `${APP_WEBVIEW_PATH}?url=${encodeURIComponent(
          path
        )}&title=${title}`,
        options: {
          title,
        },
      },
      beforeMessage
    );
  },
  createH5RouteUrl(path, options) {
    const h5Path = `${path}${path.indexOf("?") > -1 ? "&" : "?"}${qs.stringify(
      options || {}
    )}`;
    return `${APP_WEBVIEW_PATH}?url=${encodeURIComponent(h5Path)}&title=${
      options.title || ""
    }`;
  },
  /**
   * 注册原生路由配置
   * @param {Record<string, string | function>} routeConfig
   */
  registerRouteConfig(routeConfig) {
    navigator.nativeRouteMap = {
      ...navigator.nativeRouteMap,
      ...routeConfig,
    };
    return this;
  },
};

export default navigator;
